package com.yuke.cloud.service.tmc.config;

import com.yuke.cloud.common.base.dto.LoginAuthDto;
import com.yuke.cloud.common.base.dto.UserTokenDto;
import com.yuke.cloud.common.util.PublicUtil;
import com.yuke.cloud.common.util.RedisKeyUtil;
import com.yuke.cloud.service.tmc.entity.User;
import com.yuke.cloud.service.tmc.util.MyChannelInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.server.HandshakeInterceptor;
import org.springframework.web.socket.server.support.DefaultHandshakeHandler;

import javax.annotation.Resource;
import java.security.Principal;
import java.util.Map;

@Configuration
@EnableWebSocketMessageBroker
@Slf4j
public class WebSocketStompConfig implements WebSocketMessageBrokerConfigurer {

    @Value("${spring.rabbitmq.host}")
    private String host;

    @Value("${spring.rabbitmq.port}")
    private Integer port;

    @Value("${spring.rabbitmq.username}")
    private String userName;

    @Value("${spring.rabbitmq.password}")
    private String password;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 注册stomp站点,将 "/webSocketServer" 注册为一个 STOMP 端点。这个路径与发送和接收消息的目的地路径有所
     * 不同。这是一个端点，客户端在订阅或发布消息到目的地路径前，要连接到该端点。
     * @param registry
     */
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        //允许使用socketJs方式访问，访问点为webSocketServer，允许跨域
        //在网页上我们就可以通过这个链接
        //ws://127.0.0.1:8030/webSocketServer来和服务器的WebSocket连接
        registry.addEndpoint("/webSocketServer").addInterceptors(new HandshakeInterceptor() {
            /**
             * websocket握手
             */
            @Override
            public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
                log.info("websocket握手beforeHandshake:{}",request);
                ServletServerHttpRequest req = (ServletServerHttpRequest) request;
                //获取token认证
                String token = req.getServletRequest().getParameter("token");
                //解析token获取用户信息
               Principal user = parseToken(token);
                log.info("token认证user:{}",user);
                if(user == null){   //如果token认证失败user为null，返回false拒绝握手
                    return false;
                }
                //保存认证用户
                attributes.put("user", user);

                return true;
            }

            @Override
            public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {

            }
        }).setHandshakeHandler(new DefaultHandshakeHandler(){
            @Override
            protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) {
                //设置认证用户
                return (Principal)attributes.get("user");
            }
        }).setAllowedOrigins("*").withSockJS();
    }

    /**
     * 根据token认证授权
     * @param token
     */
    private Principal parseToken(String token){
        //从redis中获取用户信息
//        log.info("==========parseToken.token:{}", token);
        if (PublicUtil.isNotEmpty(token)) {
            LoginAuthDto loginUser = (UserTokenDto) redisTemplate.opsForValue().get(RedisKeyUtil.getAccessTokenKey(token));
            if (loginUser != null) {
                return new User(token);
            }
        }

        return null;
    }

    /**
     * 配置信息代理
     * @param registry
     */
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        //基于内存的STOMP消息代理来代替mq的消息代理
        //订阅Broker名称,/queue代表点对点即发指定用户，/topic代表发布广播即群发
        registry.enableSimpleBroker("/queue", "/topic","/user");

        //基于RabbitMQ 的STOMP消息代理
/*        registry.enableStompBrokerRelay("/queue", "/topic")
                .setRelayHost(host)
                .setRelayPort(port)
                .setClientLogin(userName)
                .setClientPasscode(password);*/

        //全局使用的消息前缀（客户端订阅路径上会体现出来）
        //例如客户端发送消息的目的地为/app/sendTest，则对应控制层@MessageMapping(“/sendTest”)
        registry.setApplicationDestinationPrefixes("/app");
        //点对点使用的订阅前缀，不设置的话，默认也是/user/
        registry.setUserDestinationPrefix("/user");
    }

    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        registration.interceptors(new MyChannelInterceptor());
    }
}
